package peer

import (
	"strings"
	"sync"
	"time"

	"hundsun.com/hsl/hschain/common/types"
	"hundsun.com/hsl/hschain/p2p/common/constant"

	"hundsun.com/hsl/hschain/p2p/impl/dht/protocol"
)

var once sync.Once

// Protocol ...
type Protocol struct {
	*protocol.P2PEnv
	refreshing int32
	// "/ip4/{ip}/tcp/{port}"
	externalAddr string
	mutex        sync.Mutex
}

func init() {
	protocol.RegisterProtocolIniter(InitProtocol)
}

// InitProtocol init protocol
func InitProtocol(env *protocol.P2PEnv) {
	p := &Protocol{}
	p.Init(env)
}

//Init Init
func (p *Protocol) Init(env *protocol.P2PEnv) {
	p.P2PEnv = env
	//先进行外部地址预设置
	for _, multiAddr := range p.P2pNode.Host.Addrs() {
		addr := multiAddr.String()
		// 如果找到公网ip直接break，否则预设置一个内网ip
		if isPublicIP(strings.Split(addr, "/")[2]) {
			p.mutex.Lock()
			p.externalAddr = addr
			p.mutex.Unlock()
			break
		}
		if !strings.Contains(addr, "127.0.0.1") && !strings.Contains(addr, "localhost") {
			p.mutex.Lock()
			p.externalAddr = addr
			p.mutex.Unlock()
		}
	}

	once.Do(func() {
		protocol.RegisterStreamHandler(p.P2pNode.Host, constant.PeerInfoAddress, p.P2pNode.SyncMsgResp(p.handleStreamPeerInfo))
		protocol.RegisterStreamHandler(p.P2pNode.Host, constant.Consensus, p.P2pNode.AsyncMsgResp(p.handleStreamConsensus))
		protocol.RegisterStreamHandler(p.P2pNode.Host, constant.ConsensusSync, p.P2pNode.SyncMsgResp(p.handleStreamConsensusSync))

		protocol.RegisterEventHandler(types.EventPeerInfo, p.handleEventPeerInfo)
		protocol.RegisterEventHandler(types.EventConsensusRequestMsg, p.handleEventConsensus)
		protocol.RegisterEventHandler(types.EventConsensusSyncRequestMsg, p.handleEventConsensusSync)
		protocol.RegisterEventHandler(types.EventPeerID, p.handleEventPeerID)
	})

	go func() {
		ticker1 := time.NewTicker(time.Second * 1)
		for {
			select {
			case <-p.Ctx.Done():
				p.Log.Warnf("refresh peerInfo task stopped")
				return
			case <-ticker1.C:
				go p.refreshPeerInfo()
				go p.refreshLocalPeerInfo()
			}
		}
	}()
}

func (p *Protocol) checkDone() bool {
	select {
	case <-p.Ctx.Done():
		return true
	default:
		return false
	}
}
